using System;
using System.Collections.Generic;
using System.IO;

namespace Gloom
{
	public static class ManagedGLTypeTranslator
	{
		// Information for this based on OpenTK's csharp.tm -- thanks, guys.
		private static Dictionary<string, string> typeMap = new Dictionary<string, string>()
		{ { "GLsizei", "Int32" },
			{ "GLsizeiptr", "IntPtr" },
			{ "GLintptr", "IntPtr" },
			{ "GLenum", "uint" },  // Note enums are treated specially in the translation process!
			{ "GLboolean", "bool" },
			{ "GLbitfield", "UInt32" },
			{ "GLchar", "byte" }, // OpenTK says Char, but c# chars are 16-bit!! (GLchar = C char = 8-bits.)
			{ "GLbyte", "SByte" }, // lol signed bytes
			{ "GLubyte", "byte" },
			{ "GLshort", "Int16" },
			{ "GLushort", "UInt16" },
			{ "GLint", "Int32" },
			{ "GLuint", "UInt32" },
			{ "GLfloat", "float" },
			{ "GLclampf", "float" }, // TODO: Make note in docstrings that valid range is 0.0 to 1.0??
			{ "GLdouble", "double" },
			{ "GLclampd", "double" },
			{ "GLstring", "string" },
			//{ "const GLUbyte *", "IntPtr" }, // i.e. GLstring again
			{ "GLsizeiptrARB", "IntPtr" },
			{ "GLintptrARB", "IntPtr" },
			{ "GLhandleARB", "UInt32" },
			{ "GLcharARB", "byte" },
			{ "GLint64EXT", "Int64" },
			{ "GLuint64EXT", "UInt64" },
			{ "GLint64", "Int64" },
			{ "GLuint64", "UInt64" },
			{ "GLsync", "IntPtr" },
			{ "void", "void" },
			{ "GLvoid", "void" },
			{ "Void", "void" } };
		
		public static bool TryTranslate(string rawType, string inOrOut, string valueOrArray, out string outType, StreamWriter infoLog, StreamWriter errorLog)
		{
			infoLog.WriteLine("ManagedGLTypeTranslator.TryTranslate " + rawType + " " + inOrOut + " " + valueOrArray);
			
			bool firstOk = tryFirstPass(rawType, out outType, infoLog, errorLog);
			bool secondOk = false;
			
			if (firstOk)
			{
				// Short-circuit: array of void == IntPtr!
				if (valueOrArray == "array" && outType == "GLvoid")
				{
					infoLog.WriteLine("ManagedGLTypeTranslator.TryTranslate:  It's an array of void (i.e. void*) so IntPtr is appropriate here. Easy.");
					secondOk = true;
					outType = "IntPtr";
					valueOrArray = "value"; // ... so we don't get a [] on the end
				}
				else if (outType == "GLenum")
				{
					infoLog.WriteLine("ManagedGLTypeTranslator.TryTranslate: Got an enum, so trying to use managed enum param type instead of uint here.");
					secondOk = true;
					outType = EnumSpecParser.NeatenEnumName(rawType);
					
					if (!EnumSpecParser.SuccessEnums.Contains(outType))
					{
						//errorLog.WriteLine("ManagedGLTypeTranslator.TryTranslate: Couldn't find enum `" + outType + "' in EnumSpecParser.SuccessEnums!  Failing this func.");
						infoLog.WriteLine("ManagedGLTypeTranslator.TryTranslate: Couldn't find enum `" + outType + "' in EnumSpecParser.SuccessEnums!  Using the GLEnum super-enum instead.");
						outType = "GLEnum";
					}
				}
				else
					secondOk = tryTranslatePrimitiveType(outType, out outType, infoLog, errorLog);
			}
			
			if (firstOk && secondOk)
			{
				if (inOrOut == "out" && valueOrArray == "array")
				{
					infoLog.WriteLine("ManagedGLTypeTranslator.TryTranslate: out array, so adding out keywords and []");
					outType = "out " + outType + "[]";
				}
				else
				{
					if (inOrOut == "out")
					{
						infoLog.WriteLine("ManagedGLTypeTranslator.TryTranslate: it's an out type, so adding an out keyword to the front of the produced type.");
						outType = "out " + outType;
					}
					if (valueOrArray == "array")
					{
						infoLog.WriteLine("ManagedGLTypeTranslator.TryTranslate: it's an array type, so adding [] to the end of the produced type.");
						outType += "[]";
					}
				}
				return true;
			}
			else
				return false;
		}
		
		private static bool tryFirstPass(string rawType, out string glPrimTypeOut, StreamWriter infoLog, StreamWriter errorLog)
		{
			glPrimTypeOut = "FAIL";

			infoLog.WriteLine("ManagedGLTypeTranslator.tryFirstPass " + rawType + " value");
			bool b = GLTypePrimitiveMapper.TryMap(rawType, out glPrimTypeOut, infoLog, errorLog);
			
			// Is it a pointer? (e.g. `const GLubyte *')
			if (b && glPrimTypeOut.EndsWith("*"))
			{
				infoLog.WriteLine("ManagedGLTypeTranslator.tryFirstPass: GLTypePrimitiveMapper.TryMap() converted `" + rawType + "' to `" + glPrimTypeOut + "' (pointer!), so we'll just put this as an IntPtr/GLvoid.");
				infoLog.WriteLine("Not too sure what to do here: I GIVE UP, JUST USING INTPTR. D:");
				glPrimTypeOut = "GLintptr";
			}
			
			return b;
		}
		
		private static bool tryTranslatePrimitiveType(string glPrimitiveType, out string dotNetType, StreamWriter infoLog, StreamWriter errorLog)
		{
			dotNetType = "";
			
			if (typeMap.ContainsKey(glPrimitiveType))
			{
				dotNetType = typeMap[glPrimitiveType];
				return true;
			}
			else
			{
				errorLog.WriteLine("There is no OpenGL>CSharp primitive type mapping for this type (`" + glPrimitiveType + "') in Gloom/SpecLoad/PrimitiveTypeMapper.cs.");
				infoLog.WriteLine("There is no OpenGL>CSharp primitive type mapping for this type (`" + glPrimitiveType + "') in Gloom/SpecLoad/PrimitiveTypeMapper.cs.");
				return false;
			}
		}
	}
}

